package com.jhws.core.dataUpdate.service;

import com.alibaba.fastjson.JSON;
import com.jhws.common.base.bo.BaseJson;
import com.jhws.core.dataUpdate.Dao.IDataUpdateDao;
import com.jhws.core.dataUpdate.bo.DataUpdateConstant;
import com.jhws.core.dataUpdate.bo.DataUpdateObject;
import com.jhws.core.dataUpdate.bo.IDataUpdateModule;
import com.jhws.core.push.PushXinGe;
import com.jhws.util.DateUtil;
import com.jhws.util.LogHelper;
import com.jhws.util.StringUtils;

import org.json.JSONObject;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class DataUpdateService {

    Map<String,IDataUpdateModule> modules;

    @Resource
    IDataUpdateDao dataUpdateDao;

    private void moduleLegalAssert(String module) throws Exception{
        if(modules.get(module.toLowerCase()) == null)
            throw new Exception("无法识别的模块"+module);
    }

    public void registerModule(IDataUpdateModule module){
        if(modules == null){
            modules = new HashMap<>();
        }

        for(String name : module.getModuleName())
            modules.put(name.toLowerCase(),module);
    }


    /**
     * 给账号或标签推送某个模块的数据更新事件
     * @param timeStamp  数据更新发生的时间点，要<=数据库中写入的那个时间
     * @param module     产生数据的模块
     * @param label      推送标签
     * @param isAccount  是否按账号推送
     * @param title      数据标题
     * @param content    数据内容
     * @throws Exception
     */
    public void pushAndUpdate(Long timeStamp,String module,String label,boolean isAccount, boolean ios, String title, Object content) throws Exception{
        module = module.toLowerCase();
        moduleLegalAssert(module);
        push(module,label,isAccount?DataUpdateConstant.PUSH_TYPE_ACCOUNT:DataUpdateConstant.PUSH_TYPE_LABEL,ios,title,content);
        /* 传入的timeStamp ，就是数据更新发生的时间点，亦是数据库中数据的时间戳字段所存数据
        *  为什么要-1，因为数据库中是对比时间戳大小，账号模式会直接拿这个timeStamp去对比，而标签模式由于拉取一次后
        *  用户所存模块时间戳会等于这个timeStamp, 所以它会影响第二次标签拉取，能把第一次的数据再拉一遍，确保没有并发问题导致数据遗漏，重复数据由终端过滤
        * */
        dataUpdateDao.setTimeStamp(label,module, Long.toString(timeStamp - 1),isAccount);
    }

    /**
     * 推送接口，不维护时间戳
     * @param module
     * @param label
     * @param ios
     * @param title
     * @param content
     */
    public void push(String module,String label,Integer pushType, boolean ios, String title, Object content){
        Map iosCustom = new HashMap<String, Object>();
        if(ios) {
            iosCustom.put("module", module);
            iosCustom.put("NT", content);
        }

        Map androidCustom = new HashMap<String,Object>();
        androidCustom.put("module",module);
        if(content != null)
            androidCustom.put("data",content);

        String androidContent = JSON.toJSONString(new BaseJson<>(content,module,1));

        if(pushType == DataUpdateConstant.PUSH_TYPE_ACCOUNT) {
            PushXinGe.androidPushSingleAccountWithCustom(label, title, androidContent,androidCustom);
            if(ios) {
                JSONObject jsonObject = null;
                /* 内容为字符串 则推送标题加内容 */
                if(content instanceof String && StringUtils.isNotEmpty((String)content)){
                    Map alert = new HashMap();
                    alert.put("title",title);
                    alert.put("body",content);
                    jsonObject = new JSONObject(alert);
                }
                PushXinGe.iosPushSingleAccountWithCustom(label, jsonObject!=null?jsonObject:title, iosCustom);
            }
        }else if(pushType == DataUpdateConstant.PUSH_TYPE_LABEL){
            PushXinGe.androidPushTagWithCustom(androidContent,title,label,androidCustom);
            if(ios)
                PushXinGe.iosPushTagWithCustom(title,label,iosCustom);
        }
    }

    /**
     * 只更新时间戳,不推送
     * @param timeStamp
     * @param module
     * @param label
     * @param isAccount
     * @throws Exception
     */
    public void update(Long timeStamp,String module,String label,boolean isAccount) throws Exception{
        module = module.toLowerCase();
        moduleLegalAssert(module);
        dataUpdateDao.setTimeStamp(label,module, Long.toString(timeStamp - 1),isAccount);
    }

    /**
     * 获取用户某个模块的更新数据
     * @param username      账号名
     * @param module        模块名
     * @param label         标签名
     * @param moduleParams  模块自定义参数
     * @return              List对象
     * @throws Exception
     */
    public DataUpdateObject getUserUpdateDataForModule(String username, String ownerCode, String module, String label, Map<String,String> moduleParams) throws Exception{
        module = module.toLowerCase();
        if(modules.get(module) == null) {
            LogHelper.e("模块 "+module+" 不存在");
            return null;
        }

        String force = moduleParams.get("force");

        /* 将force转成毫秒 */
        if(force !=null && !force.equals("default")) {
            Long lforce = Long.valueOf(force);
            if(force.length() < 13)
                force = Long.valueOf(lforce * 1000).toString();
        }

        /* 优先将force值作为拉取时间依据,强制拉取某个时间区段的数据 */
        String rTimeStamp = force;
        String userUpdateTimeStamp = force;
        String labelUpdateTimeStamp = null;
        Long newTimestamp = System.currentTimeMillis();

        /* 假如force为默认设置，则默认拉取一个月的数据 */
        if(force != null && force.equals("default")){
            rTimeStamp = Long.toString(getDefaultBeginTimestamp());
            userUpdateTimeStamp = Long.toString(getDefaultBeginTimestamp());
            labelUpdateTimeStamp = null;
        }
        /* 如果没带force参数,按照服务端记录的时间线来拉取更新的数据 */
        else if(force == null){
        /* 获取用户上一次读取该标签下该模块数据的时间戳 */
            rTimeStamp = dataUpdateDao.getUserReadTimeStamp(username, label, module);
        /* 获取推送给该用户的专属数据的时间戳 */
            userUpdateTimeStamp = dataUpdateDao.getTimeStamp(username, module);
        /* 获取该标签下该模块的数据最近更新的时间戳 */
            labelUpdateTimeStamp = dataUpdateDao.getTimeStamp(label, module);
        }

        DataUpdateObject userDataList = null;
        DataUpdateObject labelDataList = null;

        List retList = new ArrayList();
        DataUpdateObject retObj = new DataUpdateObject(retList,0);

        /* 假设用户专有推送的时间戳不为空，则需要单独去拉取该模块下该用户的数据 */
        if(StringUtils.isNotEmpty(userUpdateTimeStamp)){
            userDataList = modules.get(module).getUserModuleData(username,ownerCode,Long.valueOf(userUpdateTimeStamp),module,moduleParams);
            /* 如果取数据成功,则移除时间戳,下次就不会重复来取这个数据,
            可能出现不成功的情况,因为用户可能属于多种角色,拉取不成功,时间戳保留，等待下次拉取 */
            if(!DataUpdateObjectEmpty(userDataList) || (userDataList !=null && userDataList.getSize() != 0))
                dataUpdateDao.removeUserTimeStamp(username,module);
        }

        if(StringUtils.isNotEmpty(rTimeStamp)){
            if(StringUtils.isNotEmpty(labelUpdateTimeStamp) && Long.valueOf(rTimeStamp) < Long.valueOf(labelUpdateTimeStamp)){
                /* 用户读取时间小于标签数据更新时间，则拉取数据，且将读取时间设置为标签数据更新时间 */
                labelDataList = modules.get(module).getLabelModuleData(label,Long.valueOf(rTimeStamp),module,moduleParams);
                if(labelDataList !=null && labelDataList.getDatalist() !=null && labelDataList.getDatalist().size()!=0)
                    dataUpdateDao.setUserReadTimeStamp(username,label,module,labelUpdateTimeStamp);
            }
            else if(StringUtils.isEmpty(labelUpdateTimeStamp)){
                labelDataList = modules.get(module).getLabelModuleData(label,Long.valueOf(rTimeStamp),module,moduleParams);
                if(labelDataList !=null && labelDataList.getDatalist() !=null && labelDataList.getDatalist().size()!=0)
                    dataUpdateDao.setUserReadTimeStamp(username,label,module,Long.toString(newTimestamp));
                else /* 假如标签没有读到任何数据, 且标签时间又为空, 则强制写入一个标签时间，防止重复空读 */
                    dataUpdateDao.setTimeStamp(label,module,Long.toString(newTimestamp),false);
            }
        }else{
            /* 假设服务端并没有用户上次读取这个模块数据的记录，则默认读取最近一个月的数据 */
            dataUpdateDao.setUserReadTimeStamp(username,label,module,Long.toString(System.currentTimeMillis()));
            labelDataList = modules.get(module).getLabelModuleData(label,getDefaultBeginTimestamp(),module,moduleParams);
        }

        if(!DataUpdateObjectEmpty(userDataList))
            retObj.getDatalist().addAll(userDataList.getDatalist());
        if(!DataUpdateObjectEmpty(labelDataList))
            retObj.getDatalist().addAll(labelDataList.getDatalist());

        if(userDataList != null)
            retObj.setSize(retObj.getSize() + userDataList.getSize());

        if(labelDataList != null)
            retObj.setSize(retObj.getSize() + labelDataList.getSize());
        return retObj;
    }

    private Long getDefaultBeginTimestamp(){
        return (DateUtil.getTimeStampsNowFmtSec() - 30*24*3600)*1000;
    }

    private boolean DataUpdateObjectEmpty(DataUpdateObject obj){
        return obj == null || obj.getDatalist() == null || obj.getDatalist().size() == 0;
    }
}
